package com.dhy.dbCore.util;

import com.dhy.dbCore.anno.ParamMapping;
import com.mysql.jdbc.JDBC42ResultSet;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;


/**
 * @author 大忽悠
 * @create 2022/6/6 21:52
 */
@Slf4j
public class BeanUtils {
    public static <T> List<T> autoWiredList(ResultSet resultSet, Class<T> targetClass) {
        List<T> resList = null;
        try {
            resList = new ArrayList<>();
            if (targetClass != null) {
                Field[] fields = targetClass.getDeclaredFields();
                doAutoWiredByTargetClass(resultSet, targetClass, resList);
            } else {
                doAutoWiredByMap(resultSet, resList);
            }
        } catch (SQLException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            log.error("属性注入报错: ", e);
        } finally {
            try {
                resultSet.close();
            } catch (SQLException e) {
                log.error("属性注入报错: ", e);
            }
        }
        return resList;
    }

    /**
     * 集合中每个元素是一个map集合
     */
    private static void doAutoWiredByMap(ResultSet resultSet, List resList) throws SQLException, NoSuchFieldException, IllegalAccessException {
        JDBC42ResultSet jdbc42ResultSet = (JDBC42ResultSet) resultSet;
        com.mysql.jdbc.Field[] columns = getColumnMetadata(jdbc42ResultSet);
        while (jdbc42ResultSet.next()) {
            HashMap<String, String> hashMap = new HashMap<>();
            for (int i = 1; i <=columns.length; i++) {
                com.mysql.jdbc.Field field = columns[i - 1];
                String key = field.getName();
                String val = jdbc42ResultSet.getString(i);
                hashMap.put(key,val);
            }
            resList.add(hashMap);
        }
    }


    private static com.mysql.jdbc.Field[] getColumnMetadata(JDBC42ResultSet jdbc42ResultSet) throws IllegalAccessException {
        Field[] allFields = getAllFields(jdbc42ResultSet);
        Field targetFiled = findTargetFiled("fields", allFields);
        targetFiled.setAccessible(true);
        return ((com.mysql.jdbc.Field[]) targetFiled.get(jdbc42ResultSet));
    }

    private static <T> void doAutoWiredByTargetClass(ResultSet resultSet, Class<T> targetClass, List<T> resList) throws InstantiationException, IllegalAccessException, SQLException {
        Field[] fields = targetClass.getDeclaredFields();
        while (resultSet.next()) {
            T target = targetClass.newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                Class<?> type = field.getType();
                try {
                    Object val = getVal(resultSet, field, type);
                    if (val == null) {
                        continue;
                    }
                    //处理属性上的@ParamMapping注解
                    handleParamMapping(field, val, target, type);
                } catch (SQLException e) {
                    log.error(e.getMessage());
                }

            }
            resList.add(target);
        }
    }

    private static Object getVal(ResultSet resultSet, Field field, Class<?> type) throws SQLException {
        Object val = null;
        if (Integer.class.equals(type)) {
            val = resultSet.getInt(field.getName());
        } else if (String.class.equals(type)) {
            val = resultSet.getString(field.getName());
        } else if (Double.class.equals(type)) {
            val = resultSet.getDouble(field.getName());
        } else if (Byte.class.equals(type)) {
            val = resultSet.getByte(field.getName());
        } else {
            log.error("暂不支持的数据类型: {}", type);
        }
        return val;
    }

    private static <T> void handleParamMapping(Field field, Object val, T target, Class<?> type) throws IllegalAccessException {
        if (!field.isAnnotationPresent(ParamMapping.class)) {
            field.set(target, val);
            return;
        }
        ParamMapping paramMapping = field.getAnnotation(ParamMapping.class);
        String value = paramMapping.value();
        if (value.isEmpty()) {
            field.set(target, val);
            return;
        }
        String[] keyValMappings = value.split(";");
        for (String keyValMapping : keyValMappings) {
            String[] keyVal = keyValMapping.split("=");
            if (keyVal.length != 2) {
                continue;
            }
            if (String.valueOf(val).equals(keyVal[0])) {
                field.set(target, type.cast(keyVal[1]));
                return;
            }
        }
    }

    private static Field findTargetFiled(String fieldName, Field[] fields) {
        for (Field field : fields) {
            if (field.getName().equals(fieldName)) {
                return field;
            }
        }
        return null;
    }

    public static Field[] getAllFields(Object object) {
        Class clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }
}
